/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { FrequentedBlock } from './frequented_block';
import { Inst } from './inst';

export class BasicBlock {
  index: number;
  frequency: number;

  _insts: Inst[] = new Array();
  _successors: FrequentedBlock[] = new Array();
  get successors(): FrequentedBlock[] {
    return this._successors;
  }
  set successors(newValue: FrequentedBlock[]) {
    this._successors = newValue;
  }
  _predecessors: BasicBlock[] = new Array();

  constructor(index: number, frequency: number) {
    this.index = index;
    this.frequency = frequency;
  }

  get size(): number {
    return this._insts.length;
  }

  at(index: number): Inst {
    if (index >= this._insts.length) {
      throw new Error('Out of bounds access');
    }
    return this._insts[index];
  }

  get(index: number): Inst | null {
    if (index < 0 || index >= this._insts.length) {
      return null;
    }
    return this._insts[index];
  }

  last(): Inst {
    return this._insts[this._insts.length - 1];
  }

  get insts(): Inst[] {
    return this._insts;
  }

  append(inst: Inst): void {
    this._insts.push(inst);
  }

  numSuccessors(): number {
    return this.successors.length;
  }

  successor(index: number): FrequentedBlock {
    return this.successors[index];
  }

  successorBlock(index: number): FrequentedBlock {
    return this.successors[index];
  }

  successorBlocks(): FrequentedBlock[] {
    return this.successors;
  }

  get numPredecessors(): number {
    return this._predecessors.length;
  }

  predecessor(index: number): BasicBlock {
    return this._predecessors[index];
  }

  get predecessors(): BasicBlock[] {
    return this._predecessors;
  }

  set predecessors(newValue: BasicBlock[]) {
    this._predecessors = newValue;
  }

  toString(): string {
    return `#${this.index}`;
  }

  headerString(): string {
    let result = `BB${this}:;frequency = ${this.frequency}}`;
    if (this._predecessors.length > 0) {
      let temp = '';
      for (let block of this._predecessors) {
        temp += `${block.index},`;
      }
      result += `Predecessors:${temp}\n`;
    }
    return result;
  }

  footerString(): string {
    let result = '';
    if (this.successors.length > 0) {
      let temp = '';
      for (let block of this.successors) {
        temp += block.frequency + ',';
      }
      result += `Successors: ${temp}\n`;
    }
    return result;
  }

  toStringDeep(): string {
    let result = '';
    result += this.headerString();

    for (let inst of this._insts) {
      result += `   \"${inst}"\n`;
    }
    result += this.footerString();
    return result;
  }

  [Symbol.iterator](): Iterator<Inst> {
    return this.insts[Symbol.iterator]();
  }
}
